iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 19
1
Modern Web

寫JS30天系列 第 19

JS30 - 19 - Webcam Fun - part 1

  • 分享至 

  • xImage
  •  

目標

  1. 我們要得到視訊鏡頭的影片、可以拍照、預覽照片和下載照片
  2. 可以為照片增加效果

我們先來觀察一下html吧

<div class="photobooth">
    <div class="controls">
      <button onClick="takePhoto()">Take Photo</button> 
    </div>
    <canvas class="photo"></canvas>
    <video class="player"></video>
    <div class="strip"></div>
</div>
<audio class="snap" src="http://wesbos.com/demos/photobooth/snap.mp3" hidden></audio>

首先我們有一個video.player,可以用來播放攝像頭拍到的影片
接著我們會將擷取到的影片畫到canvas.photo

const video = document.querySelector('.player');
function getVideo() {
    navigator.mediaDevices.getUserMedia({ video: true, audio: false })
        .then(mediaStream => {
            try {
                video.srcObject = mediaStream;
            } catch (err) {
                video.src = window.URL.createObjectURL(mediaStream);
            }
            video.play();
        });
};
getVideo();

我們要如何讀取攝像頭的影片呢?
可以使用navigator.mediaDevices來麥克風、攝影機或共享螢幕的連結
接著使用.getUserMedia()來取得許可
.getUserMedia(constraints)的constraints參數有2個
videoaudio用來說明請求media的類型
必須至少一個可以被指定,寫法是

{video: boolean, audio: boolean}

我們這邊給video屬性值設定trueaudio屬性值設定false
表示我們只要得到攝像頭的視訊,不需要麥克風的音訊
getUserMedia()會讓我們得到一個Promise物件
再用.then來得到MediaStream這個物件

原本的寫法是

video.src = window.URL.createObjectURL(mediaStream)

但是這個方法即將在2018的12月被捨棄
所以我們改用另外一個方法

video.srcObject = mediaStream;

為了避免有寫瀏覽器不支援
所以我們使用try{} catch {}
來讓第一種方法不適用時
切換成第二種方法

這種方式寫起來更簡單
HTMLMediaElementsrcobject屬性拿來存放MediaStream做其來源
最後用.play()播放video即可
不要忘了getVideo(),執行函式
這樣就可以得到攝像頭的影片了

接下來我們要將得到的影片畫到畫布上
CanvasRenderingContext2D提供課多種方式在canvas上繪製圖像
其中一個語法是我們今天可以使用的

ctx.drawImage(image, dx, dy, dWidth, dHeight);

image

可以放canvas的圖像來源

dx, dy

如果單看MDN上的解釋,可能會一知半解
其實就是「從canvas的哪裡開始畫」
如果都是0的話
就是從左上角開始畫

dWidth、dHeight

在canvas上繪製的寬度/高度,允許對繪製的圖進行縮放

我們使用setInterval來更新我們的canvas
讓攝像頭拍到的影片能一直更新在canvas上

const canvas = document.querySelector('.photo');
const ctx = canvas.getContext('2d');
function paintToCanvas() {
    const width = video.videoWidth;
    const height = video.videoHeight;
    //使canvas的像素等同於video的像素
    canvas.width = width;
    canvas.height = height;
    //每16ms更新一次canvas
    return setInterval(() => {
        ctx.drawImage(video, 0, 0, width, height);
    }, 16);
}

video.addEventListener('canplay', paintToCanvas);

再來就是拍照功能了
在按鈕被點擊後會觸發takePhoto()
首先先播放「喀嚓」的聲音吧
之前學過要播放聲音時
先設定.currentTime = 0
這樣才可以在短時間拍多張照片時
可以有多次喀嚓的聲音

<button onClick="takePhoto()">Take Photo</button>
const strip = document.querySelector('.strip');
const snap = document.querySelector('.snap');

function takePhoto() {
    //play the sound
    snap.currentTime = 0;
    snap.play();
    
    //設置圖片成dataURL的格式,存在網頁上
    const data = canvas.toDataURL('image/jpeg');
    //設置連結
    const link = document.createElement('a');
    link.href = data;
    link.setAttribute('download', 'handsome');
    link.innerHTML = `<img src="${data}" alt="Handsome Man">`
    strip.insertBefore(link, strip.firstChild);
}

toDataURL

可以將圖片儲存在網頁上

語法

canvas.toDataURL(type, encoderOptions);

type

預設為image/png,可設定為image/jpeg

encoderOptions

image/jpegimage/webp下,可以設定0~1,調整圖片的畫質
預設是0.92

並給他一個連結linkhref指定到剛剛存圖片的(toDataURL)變數data
然後<a>的屬性download設置,可以使使用者點擊連結時,下載圖片
其值則是download的預設名稱

最後只要將link這個<a> innerHTML一個img
並使用insertBefore將他顯示在.strip上即可

語法

parentNode.insertBefore(newElement, referenceElement);

在參考的element前加入新的element

以上是如何擷取攝像頭的照片到canvas,並拍照下載

明天來替canvas上影像增加效果


上一篇
JS30 - 18 - Adding up time
下一篇
JS30 - 20 - Webcam Fun -part 2
系列文
寫JS30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言